Spring 解决循环依赖的思路 循环依赖之所以能解决,核心在于:实例化(Create) 与 初始化(Populate) 是两个分开的阶段。Spring 的做法是:只要 new 出了对象,就先不管属性有没有填好,直接把这个 “半成品” 的引用(或者获取引用的工厂)暴露出去。这样,当 B 需要 A 时,拿到的虽然是空壳,但地址是正确的。为了在不同阶段管理这些 Bean,Spring 准备了三个 Map:
Spring 三级缓存解决循环依赖演示
场景:Bean A ↔ Bean B 相互依赖
Spring 寻找 Bean 的逻辑就像是在不同等级的仓库里翻找:
一级缓存:先看有没有成品。
二级缓存:没有成品,看有没有半成品(已经暴露出来的引用)。
三级缓存:还没有,看有没有生产图纸(工厂)。如果有,赶紧现场生产一个半成品,并把它提拔到二级缓存。
为什么非要 “三级” ?(二级为何不够)
三级缓存主要是为了处理 AOP(面向切面编程)。如果没有 AOP,二级缓存确实够了,new 出 A 丢进二级缓存,B 进来直接拿走,OK。如果有 AOP,A 最终交给用户的使用应该是代理对象(Proxy),而不是原始对象(Raw)。Spring 的设计初衷是:希望 Bean 在初始化之后才做 AOP,如果每个 Bean 一出生就做 AOP,性能会极差,而且很多初始化逻辑还没跑,AOP 可能会报错。
三级缓存里存的是 ObjectFactory,它的逻辑是:如果没人急着要我,我就按原计划在最后一步做 AOP;如果有人(比如 B)现在就要我,那我就提前把 AOP 做了。具体过程是:
A 实例化 :把一个 “工厂 Lambda” 丢进三级缓存。这个工厂还没运行,只是个承诺。
B 注入 A :B 去问容器要 A。
触发工厂 :容器发现三级缓存有 A 的工厂,于是问工厂:“现在 B 就要 A,你看看 A 需不需要做 AOP?”
提前拦截 :工厂根据配置,提前为 A 生成了代理对象 A_Proxy。
转移到二级 :这个 A_Proxy 被塞进二级缓存。
B 成功拿到 A_Proxy :B 注入的是正确的代理对象。
A 慢慢初始化 :A 继续走它的流水线,最后它会发现自己的代理对象已经提前生成并存在二级缓存里了,于是 A 就不再重复做 AOP 了。
所以,三级缓存的本质是:
一级缓存:存最终成品。
二级缓存:存提前生成的代理或被提前引用的原始对象。它保证了不管多少Bean找 A,拿到的都是同一个代理对象。
三级缓存:存判断逻辑。它是分水岭,决定了 AOP 是按原计划进行(最后一步),还是因为循环依赖而提前进行。
具体的代码是怎样的? 下面是 spring 三级缓存的大致逻辑(Spring 使用的是 ReentrantLock),这里使用了双重检查锁的机制,保证在多线程的环境下,对象的创建也是安全的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public Object getSingleton (String beanName) { Object bean = singletonObjects.get(beanName); if (bean == null && isSingletonCurrentlyInCreation(beanName)) { bean = earlySingletonObjects.get(beanName); if (bean == null ) { synchronized (singletonObjects) { bean = singletonObjects.get(beanName); if (bean == null ) { bean = earlySingletonObjects.get(beanName); if (bean == null ) { ObjectFactory<?> factory = singletonFactories.get(beanName); if (factory != null ) { bean = factory.getObject(); earlySingletonObjects.put(beanName, bean); singletonFactories.remove(beanName); } } } } } } return bean; }
手搓一个出来 在之前的介绍中,我们已经在 《Spring IoC 和 DI 的简单实现》 介绍了如何手搓一个简单的Sping 容器。我们将在此基础上加入 ”解决循环依赖“ 的逻辑。在此过程中,我尽量保持与 Spring 类的设计与组织额的一致性。主要改动点包括:
引入 ObjectFactory 接口:用于三级缓存的回调。
增强 AbstractBeanFactory:实现三级缓存 Map 管理和 getSingleton 双重检查锁。
重构 AbstractAutowireCapableBeanFactory:在实例化后立即暴露三级缓存,并完善 applyPropertyValues 以支持 Bean 之间的互相引用。
核心回调接口:ObjectFactory
1 2 3 4 5 6 7 8 @FunctionalInterface public interface ObjectFactory <T> { T getObject () throws Exception; }
单例注册基类:DefaultSingletonBeanRegistry,这是 Spring 源码中三级缓存最核心的所在地。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public class DefaultSingletonBeanRegistry { private final Map<String, Object> singletonObjects = new ConcurrentHashMap <>(256 ); private final Map<String, Object> earlySingletonObjects = new HashMap <>(16 ); private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap <>(16 ); private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap <>(16 )); protected Object getSingleton (String beanName, boolean allowEarlyReference) throws Exception { Object singletonObject = this .singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this .earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this .singletonObjects) { singletonObject = this .earlySingletonObjects.get(beanName); if (singletonObject == null ) { ObjectFactory<?> singletonFactory = this .singletonFactories.get(beanName); if (singletonFactory != null ) { singletonObject = singletonFactory.getObject(); this .earlySingletonObjects.put(beanName, singletonObject); this .singletonFactories.remove(beanName); } } } } } return singletonObject; } public void addSingleton (String beanName, Object singletonObject) { synchronized (this .singletonObjects) { this .singletonObjects.put(beanName, singletonObject); this .earlySingletonObjects.remove(beanName); this .singletonFactories.remove(beanName); } } protected void addSingletonFactory (String beanName, ObjectFactory<?> singletonFactory) { synchronized (this .singletonObjects) { if (!this .singletonObjects.containsKey(beanName)) { this .singletonFactories.put(beanName, singletonFactory); this .earlySingletonObjects.remove(beanName); } } } protected boolean isSingletonCurrentlyInCreation (String beanName) { return this .singletonsCurrentlyInCreation.contains(beanName); } protected void beforeSingletonCreation (String beanName) { this .singletonsCurrentlyInCreation.add(beanName); } protected void afterSingletonCreation (String beanName) { this .singletonsCurrentlyInCreation.remove(beanName); } }
抽象工厂:AbstractBeanFactory,它继承自 DefaultSingletonBeanRegistry,负责定义 getBean 的骨架。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory { @Override public Object getBean (String name) throws Exception { Object bean = getSingleton(name, true ); if (bean != null ) { return bean; } BeanDefinition beanDefinition = getBeanDefinition(name); if (beanDefinition == null ) { throw new Exception ("No bean named " + name + " is defined" ); } beforeSingletonCreation(name); try { return createBean(name, beanDefinition); } finally { afterSingletonCreation(name); } } protected abstract BeanDefinition getBeanDefinition (String beanName) ; protected abstract Object createBean (String beanName, BeanDefinition beanDefinition) throws Exception; }
自动装配工厂:AbstractAutowireCapableBeanFactory,它负责提前暴露。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory { @Override protected Object createBean (String beanName, BeanDefinition beanDefinition) throws Exception { Object bean; try { bean = createBeanInstance(beanDefinition); addSingletonFactory(beanName, () -> bean); applyPropertyValues(beanName, bean, beanDefinition); } catch (Exception e) { throw new RuntimeException ("Instantiation of bean failed" , e); } addSingleton(beanName, bean); return bean; } protected Object createBeanInstance (BeanDefinition beanDefinition) throws Exception { Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName()); return beanClass.getDeclaredConstructor().newInstance(); } protected void applyPropertyValues (String beanName, Object bean, BeanDefinition beanDefinition) throws Exception { try { for (PropertyValue pv : beanDefinition.getPropertyValues()) { String name = pv.getName(); Object value = pv.getValue(); if (value instanceof BeanReference) { BeanReference beanReference = (BeanReference) value; value = getBean(beanReference.getBeanName()); } String setterName = "set" + name.substring(0 , 1 ).toUpperCase() + name.substring(1 ); Method[] methods = bean.getClass().getMethods(); for (Method m : methods) { if (m.getName().equals(setterName)) { m.setAccessible(true ); m.invoke(bean, value); break ; } } } } catch (Exception e) { throw new Exception ("Error setting property values for bean: " + beanName, e); } } }
辅助类:BeanReference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class BeanReference { private final String beanName; public BeanReference (String beanName) { this .beanName = beanName; } public String getBeanName () { return beanName; } }
调整 XmlBeanDefinitionReader 的解析逻辑,让它能识别 ref 属性并生成 BeanReference。
我们需要升级解析逻辑,让它能识别 ref 属性并生成 BeanReference。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class XmlBeanDefinitionReader { private final BeanDefinitionRegistry registry; public XmlBeanDefinitionReader (BeanDefinitionRegistry registry) { this .registry = registry; } public void loadBeanDefinitions (Resource resource) throws Exception { try (InputStream inputStream = resource.getInputStream()) { doLoadBeanDefinitions(inputStream); } } protected void doLoadBeanDefinitions (InputStream inputStream) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(inputStream); Element root = doc.getDocumentElement(); NodeList nodes = root.getChildNodes(); for (int i = 0 ; i < nodes.getLength(); i++) { if (!(nodes.item(i) instanceof Element)) continue ; Element ele = (Element) nodes.item(i); if (!"bean" .equals(ele.getNodeName())) continue ; String id = ele.getAttribute("id" ); String className = ele.getAttribute("class" ); BeanDefinition beanDefinition = new BeanDefinition (className); NodeList propNodes = ele.getElementsByTagName("property" ); for (int j = 0 ; j < propNodes.getLength(); j++) { Element propEle = (Element) propNodes.item(j); String name = propEle.getAttribute("name" ); String value = propEle.getAttribute("value" ); String ref = propEle.getAttribute("ref" ); if (ref != null && !ref.isEmpty()) { beanDefinition.addPropertyValue(new PropertyValue (name, new BeanReference (ref))); } else { beanDefinition.addPropertyValue(new PropertyValue (name, value)); } } registry.registerBeanDefinition(id, beanDefinition); } } }
测试单元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Data public class A { private B b; private String name; @Override public String toString () { return "A{name=" + name + "}" ; } } @Data public class B { private A a; private String name; @Override public String toString () { return "B{name=" + name + "}" ; } }
1 2 3 4 5 6 7 8 9 <bean id ="beanA" class ="com.demo.entity.A" > <property name ="name" value ="IamA" /> <property name ="b" ref ="beanB" /> </bean > <bean id ="beanB" class ="com.demo.entity.B" > <property name ="name" value ="IamB" /> <property name ="a" ref ="beanA" /> </bean >
1 2 3 4 5 6 7 8 @Test public void test01 () throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext ("bean-circle-di.xml" ); A beanA = (A) context.getBean("beanA" ); B beanB = (B) context.getBean("beanB" ); System.out.println(beanA + " " + beanA.getB().getName()); System.out.println(beanB + " " + beanB.getA().getName()); }
测试结果:
1 2 A{name=IamA} IamB B{name=IamB} IamA
大功告成!